import groovy4u.Command;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;


//-------------------
//main
//-------------------

def args = [:];
def command = new MavenCommand(args);

try {
	println "execute command ...";
	long start = System.currentTimeMillis();
	command.before();
	command.execute();
	command.after();
	println "complete: " + (System.currentTimeMillis() - start) + " ms";
} catch (e) {
	command.usage(e.getClass().getName() + ": " + e.getMessage());
	println e;
}

/**
 * 1. download jar from maven repository
 * 2. create local lucene index for remote maven repository
 * 3. user can search jar
 * @author wong.tong@gmail.com
 *
 */
class MavenCommand {

	private String MAVEN_HOME = /D:\softwares\Dev\Java\maven\apache-maven-2.0.8/;
//	private String MAVEN_HOME = /D:\tools\Java Source\maven\apache-maven-2.0.8/;

	private String localRepository;		//local repository
	private def mavenSites;				//maven sites mirrors
	private def indexFolder;			//lucene index folder
	private File groovyLibFolder;		//${user.home}/.groovy/lib
	
	public usage(errors) {
		
		println """${errors}
		""";
	}
	
	private removeLastSlash(str) {
		if (str.endsWith("/")) str = str.substring(0, str.length()-1);
		return str;
	}

	/**
	 * initialize
	 */	
	public before() {
		
		def settings = this.getMavenSettings();
		this.localRepository = this.removeLastSlash(settings.localRepository.text());
		
		this.mavenSites = settings.mirrors.mirror.collect { mirror ->
			this.removeLastSlash(mirror.url.text());
		}
		
		this.indexFolder = new File(this.localRepository + File.separator + ".index");
		if (!this.indexFolder.exists()) {
			this.indexFolder.mkdirs();
		}
		
		this.groovyLibFolder = new File(System.properties['user.home']+'/.groovy/lib')
		if (!this.groovyLibFolder.exists()) {
			this.groovyLibFolder.mkdirs();
		}
	}
	
	public after() {
	}
	
	public execute() {
		
		install("groovy4u", "groovy4u", "0.0.1");
//		install("org.apache.lucene", "lucene-core", "2.4.1");
//		index();
	}
	
	/**
	 * create lucene index for jars in websites
	 */
	private index() {

		Directory directory = FSDirectory.getDirectory(this.indexFolder);
		Analyzer analyzer = new StandardAnalyzer();
		IndexWriter writer = new IndexWriter(directory, analyzer, true, MaxFieldLength.UNLIMITED);

//		def site = "http://repo1.maven.org/maven2";
//		def fullpath = "http://repo1.maven.org/maven2/antlr/antlr/2.7.1/antlr-2.7.1.jar";
//		def doc = this.createDocument(site, fullpath.substring(site.size()+1));
//		writer.addDocument(doc);
		this.mavenSites.each { site ->
			
			def queue = new LinkedList();
			queue.offer(site);
			while (!queue.isEmpty()) {
				def url = queue.poll();
				def paths = this.parseMavenWebPage(url);
				paths.each { path ->
					if (path.endsWith(".jar")) {
						def fullpath = url + "/" + path;
						println "indexing ${fullpath}";
						def doc = this.createDocument(site, fullpath.substring(site.size()+1));
						writer.addDocument(doc);
					} else if (path.endsWith("/")) {
						queue.offer(url + "/" + path);
					}
				}
			}
		}
		writer.close();
	}
	
	private createDocument(site, path) {
		
		def doc = new Document();
		
		doc.add(new Field("site", site, Field.Store.YES, Field.Index.UN_TOKENIZED));
		
		def paths = path.split("/");
		def len = paths.size();
		
		doc.add(new Field("groupId", paths[0..len-4].join("."), Field.Store.YES, Field.Index.UN_TOKENIZED));
		doc.add(new Field("artifactId", paths[len-3], Field.Store.YES, Field.Index.UN_TOKENIZED));
		doc.add(new Field("version", paths[len-2], Field.Store.YES, Field.Index.UN_TOKENIZED));
		
		return doc;
	}
	
	private parseMavenWebPage(url) {
		
		def os = new ByteArrayOutputStream();
		IOUtils.wget(url, os);
		def content = os.toString();

		def paths = [];
		(content =~ /<a href=\".*\">(.*)<\/a>(.*)-/).each { m ->
			paths.add(m[1]);
		}
		return paths;
	}
	
	/**
	 * search for jars
	 */
	private search(str) {
		
		
	}
	
	private getMavenSettings() {
		
		def confFile = new File("$MAVEN_HOME\\conf\\settings.xml");
		if (confFile.exists()) {
			return new XmlParser().parse(confFile);
		} else {
			throw new RuntimeException("Maven Configuration File '${confFile.getAbsolutePath()}' is not FOUND!");
		}
	}
	
	/**
	 * if the jar file not exists in local maven repository, then try to download it.
	 * @param groupId
	 * @param artifactId
	 * @param version
	 * @return return the file pathes as a map: 
	 *                  [".jar": jar_file, ".pom": pom_file]
	 * if download successfully, the *_file will be an File instance; 
	 * else it will be null.
	 */
	private downloadIfNotFound(groupId, artifactId, version) {
		
		def path = ["${groupId.split(/\./).join(File.separator)}",
					"${artifactId}", "${version}"].join(File.separator);

		def folder = new File(this.localRepository, path);
		if (!folder.exists()) {
			folder.mkdirs();
		}
		//download file
		def files = [:];
		[	".jar", ".jar.sha1", ".jar.md5",
			".pom", ".pom.sha1", ".pom.md5"
		].each { ext ->
			def filename = "${artifactId}-${version}" + ext;
			def file = new File(this.localRepository, path + File.separator + filename);
			def fos = null;
			if (!file.exists()) {
				//try to download from any of the mirror repository
				this.mavenSites.any { site ->
					try {
						def url = "${site}/${groupId.split(/\./).join('/')}/${artifactId}/${version}/${filename}";
						fos = new FileOutputStream(file);
						IOUtils.wget(url, fos);
						println "Downloading ${url} ...";
						files[ext] = file;
					} catch (Exception e) {
						println e.class.getName() + ": " + e.getMessage();
						files[ext] = null;
					} finally {
						if (fos != null) fos.close();
					}
				}
				if (files[ext] == null) {
					file.delete();
				}
			} else {
				files[ext] = file;
			}
		}
		//check empty folder
		if (files.every { it.value == null }) {
			folder.delete();
		}
		return files;
	}

	/**
	 * install jar for groovy
	 * @param groupId
	 * @param artifactId
	 * @param version
	 */
	private install(groupId, artifactId, version) {
		
		def ifile = this.downloadIfNotFound(groupId, artifactId, version)[".jar"];
		if (ifile != null && ifile.exists()) {
			def ofile = new File(this.groovyLibFolder, "${artifactId}-${version}.jar");
			if (!ofile.exists()) {
				def input = new FileInputStream(ifile);
				def output = new FileOutputStream(ofile);
				try {
					IOUtils.copy(input, output);
					println "installed: ${groupId}:${artifactId}:${version}"
				} catch (Exception e) {
					throw new RuntimeException("Failed to copy file: ${ifile} to ${ofile}");
				} finally {
					if (input != null) input.close();
					if (output != null) output.close();
				}
			}
		} else {
			throw new RuntimeException("Input file not found!");
		}
	}
}